import uvicorn
from fastapi import FastAPI, Request, UploadFile, File, Response, Form, Depends, HTTPException
from fastapi.security import HTTPBasic
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from fastapi.responses import RedirectResponse

from datetime import datetime
import glob
import shutil
import os
from pathlib import Path
import keras
import tensorflow as tf

# 数据库映射连接
from sqlalchemy.orm import Session
import models
from database import engine, get_db, SessionLocal

# 创建数据库表
models.Base.metadata.create_all(bind=engine)


# 依赖项
def get_data():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


# CORS中间件
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# 配置静态文件路径
app.mount("/static", StaticFiles(directory="static"), name="static")
app.mount("/Internet_img",StaticFiles(directory="Internet_birds_save"),name="Internet_img")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 全局预加载模型
model = keras.models.load_model('./model2.h5')

# 安全认证
security = HTTPBasic()
templates = Jinja2Templates(directory="templates")


# 测试数据库是否连接成功
@app.get('/Get_db')
def Get_db():
    result = get_db()
    return result


# 主页
@app.get("/")
def index(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})


# 获取前端用户上传文件
@app.post("/upload-image/")
async def upload_image(image: UploadFile = File(...)):
    # 图片名字
    image_name = image.filename
    # 图片路径
    image_path = Path.cwd() / "Internet_birds_images" / image_name

    # 检测图片文件是否存在，不存在则创建
    if not os.path.exists("Internet_birds_images"):
        os.makedirs("Internet_birds_images")
    # 检查是否存在旧文件直接删除旧文件然后进新建
    else:
        # 如果文件夹存在，则删除其中的所有文件
        files = glob.glob("Internet_birds_images/*")
        for f in files:
            os.remove(f)

    # 将上传的图片保存到文件夹里面
    with image_path.open("wb") as f:
        shutil.copyfileobj(image.file, f)

    # 鸟类分类
    bird_classes = [
        'Asian Green Bee-Eater(食蜂鸟)',
        'Brown-Headed Barbet(斑头绿拟啄木鸟)',
        'Cattle Egret(牛背鹭)',
        'Common Kingfisher(普通翠鸟)',
        'Common Myna(新西兰八哥)',
        'Common Rosefinch(普通朱雀)',
        'Common Tailorbird(普通缝叶莺)',
        'Coppersmith Barbet(赤胸拟啄木鸟)',
        'Forest Wagtail(山鹡鸰)',
        'Gray Wagtail(灰鹡鸰)',
        'Hoopoe(戴胜)',
        'House Crow(家鸦)',
        'Indian Grey Hornbill(印度灰犀鸟)',
        'Indian Peacock(印度孔雀)',
        'Indian Pitta(蓝翅八色鸫)',
        'Indian Roller(棕胸佛法僧)',
        'Jungle Babbler(丛林鸫鹛)',
        'Northern Lapwing(凤头麦鸡)',
        'Red-Wattled Lapwing(肉垂麦鸡)',
        'Ruddy Shelduck(赤麻鸭)',
        'Rufous Treepie(棕腹树鹊)',
        'Sarus Crane(赤颈鹤)',
        'White-Breasted Kingfisher(白胸翠鸟)',
        'White-Breasted Waterhen(白胸苦恶鸟)',
        'White Wagtail(白鹡鸰)'
    ]

    # 用户上传文件夹路径
    user_path = './Internet_birds_images/'
    # 对图片数据进行预处理
    img_path = os.path.join(user_path, image_name)
    img = tf.keras.preprocessing.image.load_img(img_path, target_size=(224, 224))
    img_array = tf.keras.preprocessing.image.img_to_array(img)
    img_array = img_array / 255.
    img_array = img_array.reshape(1, *img_array.shape)
    # 输出预测结果
    prediction = model.predict(img_array)[0]

    # 将预测结果和鸟类名字对应（针对测试阶段方便查看）
    # for bird, score in zip(bird_classes, prediction):
    #     print(f'{bird}:{score * 100:.2f}%')

    # 显示最有可能的鸟类
    most_likely_bird = bird_classes[prediction.argmax()]
    most_likely_bird_score = f'{prediction[prediction.argmax()] * 100 :.2f}%'
    # 分割出鸟类的名字依次放入对应名字的文件夹
    bird_flg_name = most_likely_bird.split('(')[1]
    bird_flg_name = bird_flg_name.split(')')[0]

    # 执行完以上操作后把图片按照时间保存到一个文件夹并且以时间来命名
    time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    # 将时间冒号换掉避免文件无法保存
    time = time.replace(":", "-")
    # 前提是有文件的情况下
    if os.path.exists("Internet_birds_images"):
        # 复制图片并且重命名
        shutil.copy("./Internet_birds_images/" + image_name, "./Internet_birds_save/" + time + image_name[-4:])

    # 返回一个包含图片名称和文件的URL
    return {
        "image_name": image_name,
        "image_url": f"http://localhost:8000/upload-image/{image_name}",
        # 模型训练预测结果
        "prediction": bird_flg_name,
        # 模型结果的概率
        "score": most_likely_bird_score
    }


# 用于从Internet_birds_images文件夹提供静态文件
@app.get("/upload-image/{path:path}")
async def serve_image(path: Path):
    with open(Path("Internet_birds_images") / path, 'rb') as f:
        return Response(content=f.read(), media_type='image/jpeg')


# 管理员登录页面跳转-
@app.get("/login")
async def login(request: Request):
    return templates.TemplateResponse("login.html", {"request": request})


name = []


# 管理员页面登录验证
@app.post("/login")
async def handel_login(request: Request, admin_name: str = Form(...), password: str = Form(...),
                       db: Session = Depends(get_data)):
    # 设置管理员认证逻辑
    # 获取数据库数据
    admin = db.query(models.Admin).filter(models.Admin.admin_name == admin_name).first()
    if admin_name == admin.admin_name and password == admin.password:
        # 认证成功保存管理管理员名字
        name.append(admin_name)
        # 认证成功重定向另一个页面
        return RedirectResponse(url="/admin", status_code=303)
    # 认证失败返回登录页面
    return templates.TemplateResponse("login.html", {"request": request, "error": "认证失败请检查你的账户和密码"})


# 管理页面
@app.get("/admin")
def admin(request: Request):
    # 获取已经认证的管理员名字如果没有就说明为认证
    admin_name = name[0]
    if not admin_name:
        # 没有就返回登录
        return RedirectResponse(url="/login", status_code=303)
    return templates.TemplateResponse("admin.html", {"request": request, "admin_name": admin_name})


# 数据信息
@app.get("/basic_table")
def basic_table(request: Request):
    # 获取已经认证的管理员名字如果没有就说明为认证
    admin_name = name[0]
    if not admin_name:
        # 没有就返回登录
        return RedirectResponse(url="/login", status_code=303)
    return templates.TemplateResponse("basic_table.html", {"request": request, "admin_name": admin_name})


# 数据响应信息表
@app.get("/responsive_table")
def responsive_table(request: Request):
    # 获取已经认证的管理员名字如果没有就说明为认证
    admin_name = name[0]
    if not admin_name:
        # 没有就返回登录
        return RedirectResponse(url="/login", status_code=303)

    # 获取历史图片数量
    img_list = os.listdir('./Internet_birds_save/')
    img_num = len(img_list)
    img_url = img_list[0:img_num]
    data = []
    for i in img_list:
        data.append(i[:-4])

    if img_num == 0:
        # 如果没有图片，则不显示分页和空提示信息
        return templates.TemplateResponse("responsive_table.html",
                                          {
                                              "request": request,
                                              "admin_name": admin_name,
                                              "img_num": 0
                                          })

    return templates.TemplateResponse("responsive_table.html",
                                      {
                                          "request": request,
                                          "admin_name": admin_name,
                                          "img_num": img_num,
                                          "img_url":img_url,
                                          "data":data
                                      })

#删除图片信息
@app.post("/delete_img/{img_file}")
async def delete_img(img_file: str):
    # 删除图片的路径
    img_path = "./Internet_birds_save/" + img_file
    try:
        # 检查文件是否存在并删除
        if os.path.exists(img_path):
            os.remove(img_path)
        else:
            raise HTTPException(status_code=404, detail="File not found")
        return RedirectResponse(url='/responsive_table',status_code=303)
    except Exception as e:
        # 如果发生错误，返回错误信息
        raise HTTPException(status_code=500, detail=str(e))

if __name__ == '__main__':
    # 自定义端口（测试）
    uvicorn.run(app, host="127.0.0.1", port=8080)
    index()

